﻿// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the LGPL License, Version 3.0. See License.txt in the project root for license information.
// Website: https://admin.blazor.zone

using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Primitives;
using System.Diagnostics.CodeAnalysis;

namespace BlazorAdmin.Caching.Services
{
    /// <summary>
    /// 缓存管理类
    /// </summary>
    internal class DefaultCacheManager : ICacheManager
    {
        private readonly double slidingExpirationSeconds;//内置缓存策略 缓存相对时间 15 分钟
        [NotNull]
        private IMemoryCache? Cache { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="cache"></param>
        /// <param name="slidingSeconds">滑差过期时间，单位秒</param>
        public DefaultCacheManager(IMemoryCache cache, double slidingSeconds = 15 * 60)
        {
            Cache = cache;
            slidingExpirationSeconds = slidingSeconds;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="factory"></param>
        /// <returns></returns>
        public T GetOrAdd<T>(string key, Func<ICacheEntry, T> factory) => Cache.GetOrCreate(key, entry =>
        {
            HandlerEntry(key, entry);
            return factory(entry);
        })!;

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="factory"></param>
        /// <returns></returns>
        public Task<T> GetOrAddAsync<T>(string key, Func<ICacheEntry, Task<T>> factory) => Cache.GetOrCreate(key, entry =>
        {
            HandlerEntry(key, entry);
            return factory(entry);
        })!;

        private void HandlerEntry(string key, ICacheEntry entry, IChangeToken? token = null)
        {
            if (token != null)
            {
                entry.AddExpirationToken(token);
            }
            if (entry.AbsoluteExpiration == null && entry.SlidingExpiration == null && !entry.ExpirationTokens.Any())
            {
                entry.SlidingExpiration = TimeSpan.FromMilliseconds(slidingExpirationSeconds);
            }
            entry.RegisterPostEvictionCallback((key, value, reason, state) =>
            {
                Console.WriteLine("RegisterPostEvictionCallback:{0}|{1}|{2}|{3}", key, value, reason, state);
            });
        }

        public void Clear(string? key)
        {
            if (!string.IsNullOrEmpty(key))
            {
                Cache.Remove(key);
            }
            else if (Cache is MemoryCache c)
            {
                c.Compact(100);
            }
        }
    }
}